using System;

namespace Com.FirstSolver.Splash;

public class SM4 : SymmetricAlgorithm
{
	protected uint[] mSK = new uint[36];

	protected int mRKOff = 4;

	private static readonly byte[] S = new byte[256]
	{
		214, 144, 233, 254, 204, 225, 61, 183, 22, 182,
		20, 194, 40, 251, 44, 5, 43, 103, 154, 118,
		42, 190, 4, 195, 170, 68, 19, 38, 73, 134,
		6, 153, 156, 66, 80, 244, 145, 239, 152, 122,
		51, 84, 11, 67, 237, 207, 172, 98, 228, 179,
		28, 169, 201, 8, 232, 149, 128, 223, 148, 250,
		117, 143, 63, 166, 71, 7, 167, 252, 243, 115,
		23, 186, 131, 89, 60, 25, 230, 133, 79, 168,
		104, 107, 129, 178, 113, 100, 218, 139, 248, 235,
		15, 75, 112, 86, 157, 53, 30, 36, 14, 94,
		99, 88, 209, 162, 37, 34, 124, 59, 1, 33,
		120, 135, 212, 0, 70, 87, 159, 211, 39, 82,
		76, 54, 2, 231, 160, 196, 200, 158, 234, 191,
		138, 210, 64, 199, 56, 181, 163, 247, 242, 206,
		249, 97, 21, 161, 224, 174, 93, 164, 155, 52,
		26, 85, 173, 147, 50, 48, 245, 140, 177, 227,
		29, 246, 226, 46, 130, 102, 202, 96, 192, 41,
		35, 171, 13, 83, 78, 111, 213, 219, 55, 69,
		222, 253, 142, 47, 3, 255, 106, 114, 109, 108,
		91, 81, 141, 27, 175, 146, 187, 221, 188, 127,
		17, 217, 92, 65, 31, 16, 90, 216, 10, 193,
		49, 136, 165, 205, 123, 189, 45, 116, 208, 18,
		184, 229, 180, 176, 137, 105, 151, 74, 12, 150,
		119, 126, 101, 185, 241, 9, 197, 110, 198, 132,
		24, 240, 125, 236, 58, 220, 77, 32, 121, 238,
		95, 62, 215, 203, 57, 72
	};

	private static readonly uint[] FK = new uint[4] { 2746333894u, 1453994832u, 1736282519u, 2993693404u };

	private static readonly uint[] CK = new uint[32]
	{
		462357u, 472066609u, 943670861u, 1415275113u, 1886879365u, 2358483617u, 2830087869u, 3301692121u, 3773296373u, 4228057617u,
		404694573u, 876298825u, 1347903077u, 1819507329u, 2291111581u, 2762715833u, 3234320085u, 3705924337u, 4177462797u, 337322537u,
		808926789u, 1280531041u, 1752135293u, 2223739545u, 2695343797u, 3166948049u, 3638552301u, 4110090761u, 269950501u, 741554753u,
		1213159005u, 1684763257u
	};

	public override string AlgorithmName => "SM4";

	public override int mKeySize => 128;

	public override int mKeySizeInBytes => 16;

	public override byte[] mKey
	{
		get
		{
			if (!mIsReady)
			{
				throw new UnauthorizedAccessException();
			}
			uint[] SourceKey = new uint[4]
			{
				mSK[0] ^ FK[0],
				mSK[1] ^ FK[1],
				mSK[2] ^ FK[2],
				mSK[3] ^ FK[3]
			};
			byte[] SecretKey = new byte[16];
			Utils.UInt32ToByteArray(SecretKey, 0, SourceKey, 0, 4, ByteOrder.BIG_ENDIAN);
			Array.Clear(SourceKey, 0, SourceKey.Length);
			return SecretKey;
		}
	}

	public SM4(CipherDirection direction = CipherDirection.Encryption, CipherMode mode = CipherMode.CBC, PaddingMode padding = PaddingMode.PKCS7)
		: base(direction, mode, padding)
	{
	}

	protected override bool SetEncryptKey(byte[] key)
	{
		if (key.Length != mKeySizeInBytes)
		{
			return false;
		}
		Utils.UInt32FromByteArray(mSK, 0, key, 0, 16, ByteOrder.BIG_ENDIAN);
		mSK[0] ^= FK[0];
		mSK[1] ^= FK[1];
		mSK[2] ^= FK[2];
		mSK[3] ^= FK[3];
		for (int i = 0; i < 32; i++)
		{
			uint XT = mSK[i + 1] ^ mSK[i + 2] ^ mSK[i + 3] ^ CK[i];
			XT = (uint)((S[XT >> 24] << 24) | (S[(XT >> 16) & 0xFF] << 16) | (S[(XT >> 8) & 0xFF] << 8) | S[XT & 0xFF]);
			mSK[i + 4] = mSK[i] ^ (XT ^ Utils.ROTL(XT, 13) ^ Utils.ROTL(XT, 23));
		}
		return true;
	}

	protected override bool SetDecryptKey(byte[] key)
	{
		if (!SetEncryptKey(key))
		{
			return false;
		}
		Array.Reverse(mSK, mRKOff, 32);
		return true;
	}

	protected override void TransformCore(CipherDirection direction, byte[] input, int offset, byte[] output)
	{
		uint[] X = new uint[4];
		Utils.UInt32FromByteArray(X, 0, input, offset, 16, ByteOrder.BIG_ENDIAN);
		int Index = mRKOff;
		for (int i = 0; i < 32; i++)
		{
			uint XT = X[1] ^ X[2] ^ X[3] ^ mSK[Index++];
			XT = (uint)((S[XT >> 24] << 24) | (S[(XT >> 16) & 0xFF] << 16) | (S[(XT >> 8) & 0xFF] << 8) | S[XT & 0xFF]);
			XT = X[0] ^ (XT ^ Utils.ROTL(XT, 2) ^ Utils.ROTL(XT, 10) ^ Utils.ROTL(XT, 18) ^ Utils.ROTL(XT, 24));
			X[0] = X[1];
			X[1] = X[2];
			X[2] = X[3];
			X[3] = XT;
		}
		Array.Reverse(X);
		Utils.UInt32ToByteArray(output, 0, X, 0, 4, ByteOrder.BIG_ENDIAN);
	}

	protected override void Dispose(bool disposing)
	{
		if (disposing)
		{
			Array.Clear(mSK, 0, mSK.Length);
		}
		base.Dispose(disposing);
	}
}
